package com.halzhang.android.startupnews.data.net; import android.content.Context; import android.content.Intent; import android.text.TextUtils; import android.util.Log; import com.halzhang.android.startupnews.data.Constant; import com.halzhang.android.startupnews.data.entity.SNComments; import com.halzhang.android.startupnews.data.entity.SNDiscuss; import com.halzhang.android.startupnews.data.entity.SNFeed; import com.halzhang.android.startupnews.data.entity.Status; import com.halzhang.android.startupnews.data.exception.NetworkException; import com.halzhang.android.startupnews.data.parser.BaseHTMLParser; import com.halzhang.android.startupnews.data.parser.SNCommentsParser; import com.halzhang.android.startupnews.data.parser.SNDiscussParser; import com.halzhang.android.startupnews.data.parser.SNFeedParser; import com.halzhang.android.startupnews.data.utils.SessionManager; import com.squareup.okhttp.FormEncodingBuilder; import com.squareup.okhttp.Headers; import com.squareup.okhttp.HttpUrl; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; import com.squareup.okhttp.RequestBody; import com.squareup.okhttp.Response; import com.squareup.okhttp.internal.http.OkHeaders; import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; import java.io.IOException; import java.util.List; import java.util.Map; import javax.inject.Singleton; import rx.Observable; import rx.Subscriber; import rx.functions.Action0; /** * api impl * Created by Hal on 2016/5/30. */ @Singleton public class SnApiImpl implements ISnApi { private OkHttpClient mOkHttpClient; private Context mContext; private SessionManager mSessionManager; private JsoupConnector mJsoupConnector; public SnApiImpl(OkHttpClient okHttpClient, Context context, SessionManager sessionManager, JsoupConnector jsoupConnector) { mOkHttpClient = okHttpClient; mContext = context; mSessionManager = sessionManager; mJsoupConnector = jsoupConnector; } @Override public Observable<SNFeed> getSNFeed(final String url) { return Observable.create(new Observable.OnSubscribe<SNFeed>() { @Override public void call(Subscriber<? super SNFeed> subscriber) { try { Connection conn = mJsoupConnector.newJsoupConnection(url); Document doc = conn.get(); SNFeedParser parser = new SNFeedParser(); SNFeed feed = parser.parseDocument(doc); subscriber.onNext(feed); subscriber.onCompleted(); } catch (Exception e) { subscriber.onError(e); } } }); } @Override public Observable<String> getFnid() { return Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { String loginUrl = null; try { Document doc = Jsoup.connect(Constant.NEWS_URL).get(); if (doc != null) { Elements loginElements = doc.select("a:matches(Login/Register)"); if (loginElements.size() == 1) { loginUrl = BaseHTMLParser.resolveRelativeSNURL(loginElements.first().attr( "href")); } } String fnid = null; if (!TextUtils.isEmpty(loginUrl)) { doc = Jsoup.connect(loginUrl).get(); if (doc != null) { Elements inputElements = doc.select("input[name=fnid]"); if (inputElements != null && inputElements.size() > 0) { fnid = inputElements.first().attr("value"); } } } subscriber.onNext(fnid); subscriber.onCompleted(); } catch (IOException e) { subscriber.onError(e); } } }); } @Override public Observable<String> login(final String fnid, final String username, final String password) { return Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { FormEncodingBuilder builder = new FormEncodingBuilder(); builder.addEncoded("fnid", fnid).addEncoded("u", username).addEncoded("p", password); Request request = new Request.Builder().url(Constant.LOGIN_URL).post(builder.build()) .addHeader("Accept-Language", "zh-cn") .addHeader("Accept", "*/*") .addHeader("Accept-Encoding", "gzip,deflate") .addHeader("Connection", "keep-alive") .build(); try { String user = null; Response response = mOkHttpClient.newCall(request).execute(); if (response.isSuccessful()) { Map<String, List<String>> cookiesMap = mOkHttpClient.getCookieHandler().get(request.uri(), OkHeaders.toMultimap(request.headers(), null)); if (cookiesMap.size() > 0) { List<String> cookies = cookiesMap.get("Cookie"); for (String s : cookies) { String[] cookie = TextUtils.split(s, "="); if (cookie.length == 2 && "user".equals(cookie[0])) { user = cookie[1]; break; } } } } response.body().close(); mSessionManager.storeSession(user, username); subscriber.onNext(user); subscriber.onCompleted(); } catch (IOException e) { subscriber.onError(e); } } }).doOnSubscribe(new Action0() { @Override public void call() { mSessionManager.clear(); } }); } @Override public Observable<SNComments> getSNComments(final String url) { return Observable.create(new Observable.OnSubscribe<SNComments>() { @Override public void call(Subscriber<? super SNComments> subscriber) { try { Connection conn = mJsoupConnector.newJsoupConnection(url); Document doc = conn.get(); SNCommentsParser parser = new SNCommentsParser(); SNComments comments = parser.parseDocument(doc); subscriber.onNext(comments); subscriber.onCompleted(); } catch (Exception e) { subscriber.onError(e); } } }); } @Override public Observable<Status> upVote(final String postId) { return Observable.create(new Observable.OnSubscribe<Status>() { @Override public void call(Subscriber<? super Status> subscriber) { try { HttpUrl httpUrl = new HttpUrl.Builder() .scheme(Constant.SCHEME) .host(Constant.HOST) .addPathSegment("vote") .addQueryParameter("for", postId) .addQueryParameter("dir", "up") .addQueryParameter("by", mSessionManager.getSessionId()) .addQueryParameter("auth", mSessionManager.getSessionUser()) .addQueryParameter("whence", "news") .build(); Request request = new Request.Builder().url(httpUrl).build(); Response response = mOkHttpClient.newCall(request).execute(); Status status = new Status(); if (response.isSuccessful()) { status.code = Status.CODE_SUCCESS; } else { String content = response.body().string(); if (content.contains("mismatch")) { // 用户cookie无效 status.code = Status.CODE_COOKIE_VALID; } else { status.code = Status.CODE_REPEAT; } } subscriber.onNext(status); subscriber.onCompleted(); } catch (Exception e) { subscriber.onError(e); } } }); } @Override public Observable<Status> comment(final String text, final String fnid) { return Observable.create(new Observable.OnSubscribe<Status>() { @Override public void call(Subscriber<? super Status> subscriber) { try { RequestBody body = new FormEncodingBuilder().add("fnid", fnid).add("text", text).build(); Request request = new Request.Builder().url(Constant.COMMENT_URL).post(body).build(); Response response = mOkHttpClient.newCall(request).execute(); if (response.isSuccessful()) { String refreerLocation = null; Headers headers = response.headers(); for (int i = 0; i < headers.size(); i++) { if ("Refreer-Location".equals(headers.name(i))) { refreerLocation = headers.value(i); } } Status status = new Status(); if (TextUtils.isEmpty(refreerLocation) || refreerLocation.contains("fnid")) { //Location:fnid=xxxxx Cookie失效,重新登陆 status.code = Status.CODE_COOKIE_VALID; } else if (refreerLocation.contains("item")) { status.code = Status.CODE_SUCCESS; } else { status.code = Status.CODE_FAILURE; } subscriber.onNext(status); subscriber.onCompleted(); } else { NetworkException networkException = new NetworkException(response.code(), response.message()); subscriber.onError(networkException); } } catch (Exception e) { e.printStackTrace(); subscriber.onError(e); } } }); } @Override public Observable<Boolean> logout() { return Observable.create(new Observable.OnSubscribe<Boolean>() { @Override public void call(Subscriber<? super Boolean> subscriber) { try { String logoutUrl = null; Boolean result = false; Connection conn = mJsoupConnector.newJsoupConnection(Constant.HOST + "/news"); Document doc = conn.get(); Elements elements = doc.select("a:matches(logout)"); if (elements.size() > 0) { logoutUrl = BaseHTMLParser.resolveRelativeSNURL(elements.attr("href")); } else { // 用户可能在pc注销了 mSessionManager.clear(); result = true; } if (!TextUtils.isEmpty(logoutUrl)) { Request request = new Request.Builder().url(logoutUrl).build(); Response response = mOkHttpClient.newCall(request).execute(); result = response.isSuccessful(); } subscriber.onNext(result); subscriber.onCompleted(); } catch (Exception e) { subscriber.onError(e); } } }); } @Override public Observable<SNDiscuss> getDiscuss(final String url) { return Observable.create(new Observable.OnSubscribe<SNDiscuss>() { @Override public void call(Subscriber<? super SNDiscuss> subscriber) { try { Connection conn = mJsoupConnector.newJsoupConnection(url); Document doc = conn.get(); SNDiscussParser parser = new SNDiscussParser(); SNDiscuss discuss = parser.parseDocument(doc); subscriber.onNext(discuss); subscriber.onCompleted(); } catch (Exception e) { subscriber.onError(e); } } }); } }